package jmine.tec.web.front.pages.requests;

import static jmine.tec.web.wicket.spring.SpringBeanReference.forName;

import java.io.IOException;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;

import jmine.tec.rpc.common.serialization.Serializer;
import jmine.tec.web.front.FrontException;
import jmine.tec.web.front.FrontMessages;
import jmine.tec.web.front.dao.BackOfficeRequestDAO;
import jmine.tec.web.front.dao.BackOfficeRequestRevisionDAO;
import jmine.tec.web.front.domain.BackOfficeRequest;
import jmine.tec.web.front.domain.BackOfficeRequestRevision;
import jmine.tec.web.front.domain.BackOfficeRequestRevisionOutcome;
import jmine.tec.web.front.forms.FormMetadataRegistry;
import jmine.tec.web.front.pages.form.AbstractHandledFormPanel;
import jmine.tec.web.front.pages.form.HandledFormPanelFactory;
import jmine.tec.web.wicket.BeanReference;
import jmine.tec.web.wicket.component.button.ActionSubmitLink;
import jmine.tec.web.wicket.pages.Template;
import jmine.tec.web.wicket.pages.form.FormType;

import org.apache.wicket.Page;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.PropertyListView;
import org.apache.wicket.model.CompoundPropertyModel;

import bancosys.tec.exception.BusinessException;
import bancosys.tec.exception.MessageCreator;
import bancosys.tec.persist.dao.BeanNotFoundException;
import bancosys.tec.security.SecurityService;
import bancosys.tec.security.annotation.Secure;
import bancosys.tec.security.authorization.UrlPermission;
import bancosys.tec.utils.date.Timestamp;

/**
 * Page that shows requests details and the option to send a review
 * 
 * @author israel.crisanto
 */
@Secure(id = "URL_BACK_OFFICE_REQUEST_LIST", permissionType = UrlPermission.class)
public class BackOfficeRequestViewPage extends Template {

    private BackOfficeRequest backOfficeRequest;

    private Object handledForm;

    private Page sourcePage;

    private Form<BackOfficeRequestRevision> form;

    private BackOfficeRequestRevision revisionModel;

    private BeanReference<BackOfficeRequestRevisionDAO> backOfficeRequestRevisionDAO;

    private WebMarkupContainer revisionsContainer;

    private PropertyListView<BackOfficeRequestRevision> listView;

    private ActionSubmitLink btnGravar;

    private DropDownChoice<BackOfficeRequestRevisionOutcome> outcomeList;

    private TextArea commentArea;

    private BeanReference<SecurityService> secutiryService;

    /**
     * Constructor
     * 
     * @param source source page object
     * @param pageParameters page parameters with the ID of the request to show
     */
    public BackOfficeRequestViewPage(Page source, PageParameters pageParameters) {
        super(pageParameters);
        this.sourcePage = source;
        this.initializeBeans();
        this.loadRequestByPk(this.getPageParameters().getLong("id"));
        this.createComponents();
        this.createRevisionsPanel();
    }

    /**
     * Create base model and dao
     */
    private void initializeBeans() {
        this.backOfficeRequestRevisionDAO = forName("backOfficeRequestRevisionDAO");
        this.secutiryService = forName("securityService");
        this.revisionModel = this.backOfficeRequestRevisionDAO.get().createBean();
    }

    /**
     * Show a panel with previous revision info
     */
    private void createRevisionsPanel() {
        this.revisionsContainer = new WebMarkupContainer("currentRevisionsPanel");
        this.add(this.revisionsContainer);

        this.listView = new PropertyListView<BackOfficeRequestRevision>("revisionsView") {
            @Override
            protected void populateItem(ListItem<BackOfficeRequestRevision> item) {
                item.add(new Label("timestamp"));
                item.add(new Label("outcome"));
                item.add(new Label("comment"));
            }
        };

        this.revisionsContainer.add(this.listView);
        this.loadListData();
    }

    /**
     * Load and populate listView with revisions data
     */
    private void loadListData() {
        List<BackOfficeRequestRevision> currentRevisions = this.backOfficeRequestRevisionDAO.get().findAllByRequest(this.backOfficeRequest);
        if (currentRevisions.size() > 0) {
            this.listView.setList(currentRevisions);
            this.revisionsContainer.setVisible(true);

            BackOfficeRequestRevision lastRevision = currentRevisions.get(currentRevisions.size() - 1);
            if (lastRevision.getOutcome() != BackOfficeRequestRevisionOutcome.NEUTRAL) {
                this.outcomeList.setEnabled(false);
                this.commentArea.setEnabled(false);
                this.btnGravar.setVisible(false);
            }
        } else {
            this.revisionsContainer.setVisible(false);
        }
    }

    /**
     * Load our request
     * 
     * @param requestPk request primary key
     */
    private void loadRequestByPk(long requestPk) {
        BeanReference<BackOfficeRequestDAO> backOfficeRequestDAO = forName("backOfficeRequestDAO");

        try {
            this.backOfficeRequest = backOfficeRequestDAO.get().findByPk(requestPk);
        } catch (BeanNotFoundException e) {
            throw new BusinessException(FrontMessages.REQUEST_NOT_FOUND.create());
        }
    }

    /**
     * Create page visual components
     */
    private void createComponents() {
        this.handledForm = this.deserializeForm(this.backOfficeRequest);
        this.add(this.loadPanel());

        this.form =
                new Form<BackOfficeRequestRevision>("revisionForm",
                        new CompoundPropertyModel<BackOfficeRequestRevision>(this.revisionModel));
        this.form.add(new Link<Object>("btnVoltar") {
            @Override
            public void onClick() {
                this.setResponsePage(BackOfficeRequestViewPage.this.sourcePage);
            }
        });

        this.btnGravar = new ActionSubmitLink("btnGravar") {
            @Override
            public void onAction() {
                BackOfficeRequestViewPage.this.saveRevision();
                BackOfficeRequestViewPage.this.loadListData();
            }
        };

        this.form.add(this.btnGravar);

        //
        this.outcomeList = new DropDownChoice<BackOfficeRequestRevisionOutcome>("outcome", this.findAvailableRequestOutcomes());
        this.outcomeList.setNullValid(false);
        this.form.add(this.outcomeList.setRequired(true));

        this.commentArea = new TextArea("comment");
        this.form.add(this.commentArea.setRequired(true));

        this.add(this.form);
    }

    /**
     * List every avaliable Outcome
     * 
     * @return List of revision outcome types
     */
    private List<? extends BackOfficeRequestRevisionOutcome> findAvailableRequestOutcomes() {
        return Arrays.asList(BackOfficeRequestRevisionOutcome.values());
    }

    /**
     * Save our revision outcome
     */
    protected void saveRevision() {
        BackOfficeRequestRevision revision = new BackOfficeRequestRevision();
        revision.setComment(this.revisionModel.getComment());
        revision.setOutcome(this.revisionModel.getOutcome());
        revision.setTimestamp(new Timestamp());
        revision.setUser(this.secutiryService.get().getCurrentUser());
        revision.setRequest(this.backOfficeRequest);
        this.backOfficeRequestRevisionDAO.get().save(revision);
    }

    /**
     * Load the current form panel renderer
     * 
     * @return handled form panel instance
     */
    private AbstractHandledFormPanel loadPanel() {
        HandledFormPanelFactory factory = this.loadPanelFactory();
        return factory.createPanel("formContentPanel", new CompoundPropertyModel(this.handledForm), FormType.VIEW);
    }

    /**
     * Try to find the user panel class to render our current request
     * 
     * @return handled form panel class
     */
    private HandledFormPanelFactory loadPanelFactory() {
        BeanReference<FormMetadataRegistry> requestMetadata = forName("formMetadataRegistry");
        return requestMetadata.get().find(this.handledForm).getFormPanelFactory();
    }

    /**
     * Deserialize form data
     * 
     * @param request request
     * @return deserialized form
     */
    private Object deserializeForm(BackOfficeRequest request) {
        BeanReference<Serializer> serializerBean = forName("backOfficeRequestFormSerializer");

        try {
            return serializerBean.get().deserialize(request.getFormContent());
        } catch (IOException e) {
            throw new FrontException(FrontMessages.IO_EXCEPTION_DESERIALIZING_FORM.create(), e);
        } catch (ParseException e) {
            throw new FrontException(FrontMessages.PARSE_EXCEPTION_DESERIALIZING_FORM.create(), e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected MessageCreator getHelpTextCreator() {
        return FrontMessages.REQUEST_VIEW_HELP_TEXT;
    }
}
